# -*- coding: utf-8 -*-
from pgu import engine
from objects import Player, Enemy
from constants import *
from concurrent import ThreadPoolManager, Runnable
import random

class Game(engine.Game):
    """
        Classe Assistente. Auxilia na troca entre estados do jogo.
        Com a ajuda dessa classe conseguimos fazer uma lógica diferente
        para cada estado do jogo, assim como controlar os eventos separadamente.
    """
    def __init__(self, stage=1):
        self.stage = stage

    def set_stage(self, stage):
        self.stage = stage

    def get_next_stage(self):
        return Stage(self, STAGES[self.stage])
        
    def init(self):
        display.set_caption("Star Wings")
        self.player = Player([WIDTH / 2, HEIGHT - 80], PLAYER_IMAGE)


class SpawnManager(Runnable):
    def __init__(self, clazz, group, enemys):
        self.clazz = clazz
        self.group = group
        self.enemys = enemys

    def run(self):
        index = random.choice(self.enemys)
        x = random.randrange(-100, WIDTH+100)
        if x < 0 or x > WIDTH:
            y = random.randrange(-50, HEIGHT/2)
        else:
            y = 0
        self.group.add(self.clazz([x,y],*ENEMYS[index]))

class Background(object):
    def __init__(self, args):
        self.images = []
        for i in xrange(len(args)):
            self.images.append(image.load(IMAGE +"/tile%s.png"%args[i]).convert_alpha())
            if i < len(args)-1:
                self.images.append(image.load(IMAGE +"/tile%s_%s.png"%(args[i], args[i+1])).convert_alpha())
            else:
                self.images.append(image.load(IMAGE +"/tile%s_%s.png"%(args[i], args[0])).convert_alpha())

        self.diff = 0
        self.rate = 0

    def draw(self, screen):
        height = self.images[0].get_height()
        idx = (HEIGHT + self.diff) // height + 1
        offset = HEIGHT - height * idx
        for i in xrange(idx):
            screen.blit(self.images[0], (0, height * i +offset +self.diff))

    def update(self):
        if self.rate >= 3:
            self.diff += 1
            if self.diff >= 400:
                self.diff = 0
            self.rate = 0
        self.rate += 1
                

class Stage(engine.State):
    """
        Classe Base para os stagios (fases) do jogo.
        Qualquer fase deve extender essa Classe.
        Todos os principais métodos em comum (principais) devem
        ser implementados nesta Classe.
    """            
    @property
    def player(self):
        return self.game.player

        
    def init(self):
        """
            Método chamado quando esse estado se torna ativo no jogo.
            Inicializa as variaveis do estado do jogo.

            Retornar nada desse método, ou de qualquer método que sobreescreva esse.
        """
        self.enemys = Group()
        self.events = Group()
        self.background = Background(self.value[0])
        spawn = ThreadPoolManager.getInstance().scheduleAtFixedRate(
            SpawnManager(Enemy, self.enemys, self.value[1]),0,2)

    def paint(self, screen):
        """
            Esse método é chamado Após o método init. responsavel por
            "desenhar" a interface a primeira vez. Pra ser chamado outra vez
            chame o metodo repaint. Pode ser útil para continuar do ínicio, do
            estado após o jogador perder todas as chances.

            Retornar nada desse método, ou de qualquer método que sobreescreva esse.
        """
        
        screen.fill((0,0,0))
        self.player.draw(screen)
        display.update()

    def loop(self):
        """
            Devemos controlar a lógica base do jogo nesse método.
            Caso queiramos um comportamento diferenciado para alguma
            fase do jogo, devemos sobreescrever esse método.

            Devemos apenas retornar objetos do tipo engine.State desse método,
            pois o objeto que retornamos desse método será o novo "State" do jogo.
        """
        self.player.shots.update()
        self.player.update()
        self.events.update()
        self.enemys.update(self.player)
        for sprite in groupcollide(self.enemys, self.player.shots, True, True).keys():
            sprite.on_destruction(self.events)
        for sprite in spritecollide(self.player, self.enemys, True):
            sprite.on_destruction(self.events)
        self.background.update()
    
    def update(self, screen):
        """
            Atualiza todos os objetos no jogo.

            Retornar nada desse método, ou de qualquer método que sobreescreva esse.
        """
        
        screen.fill((0,0,0))
        self.background.draw(screen)
        self.player.shots.draw(screen)
        self.player.draw(screen)
        self.enemys.draw(screen)
        for event in self.events:
            event.draw(screen)
        display.update()

    def event(self, evt):
        """
            Controla os eventos recebidos do jogador.

            Devemos apenas retornar objetos do tipo engine.State desse método,
            pois o objeto que retornamos desse método será o novo "State" do jogo.
        """
        
        if evt.type == KEYDOWN:
            if evt.key == K_w or evt.key == K_UP:
                self.player.set_yacc(-5)
            elif evt.key == K_s or evt.key == K_DOWN:
                self.player.set_yacc(5)
            elif evt.key == K_a or evt.key == K_LEFT:
                self.player.set_xacc(-5)
            elif evt.key == K_d or evt.key == K_RIGHT:
                self.player.set_xacc(5)
            elif evt.key == K_RETURN or evt.key == K_SPACE:
                self.player.set_isShotting(True)
                
        elif evt.type == KEYUP:
            if evt.key == K_w or evt.key == K_UP:
                self.player.set_yacc(5)
            elif evt.key == K_s or evt.key == K_DOWN:
                self.player.set_yacc(-5)
            elif evt.key == K_a or evt.key == K_LEFT:
                self.player.set_xacc(5)
            elif evt.key == K_d or evt.key == K_RIGHT:
                self.player.set_xacc(-5)
            elif evt.key == K_RETURN or evt.key == K_SPACE:
                self.player.set_isShotting(False)
             
class Menu(engine.State):
    """
        Classe que representa o Menu do jogo.
    """
    def init(self):
        """
            inicializa os atributos do Menu, como imagens ou quaisquer objetos
            que nos auxilie.
        """

        # por enquanto usaremos apenas "textos" para representar os itens do menu.
        myfont = font.SysFont("Arial", 50, bold = False, italic = True )
        self.title = myfont.render(('STAR WINGS'), 1, (55,55,200))
        self.rect_title = self.title.get_rect(center = (WIDTH/2, 100))
        
        myfont = font.SysFont("Helvetica", 30)
        self.img_start = myfont.render(('GAME START'), 1, (0,50,175))
        self.rect_start = self.img_start.get_rect(center = (WIDTH/2, 300))
        
        self.img_credits = myfont.render('CREDITS', 1, (0,50,175))
        self.rect_credits = self.img_credits.get_rect(center = (WIDTH/2, 350))
        
        self.img_endgame = myfont.render("EXIT GAME", 1, (0,50,175))
        self.rect_endgame = self.img_endgame.get_rect(center = (WIDTH/2, 400))
        self.show_credits = False                            
        
    def paint(self, screen):
        """
            Esse método é chamado Após o método init. responsavel por
            "desenhar" a interface a primeira vez. Pra ser chamado outra vez
            chame o metodo repaint. Pode ser útil para continuar do ínicio, do
            estado após o jogador perder todas as chances.

            Retornar nada desse método, ou de qualquer método que sobreescreva esse.
        """
        
        screen.fill((BLACK))
        screen.blit(self.title, self.rect_title)
        if self.show_credits:
            self.credit(screen)
        else:
            screen.blit(self.img_start, self.rect_start)
            screen.blit(self.img_credits, self.rect_credits)
            screen.blit(self.img_endgame, self.rect_endgame)
            display.update()

    def credit (self, screen):
        myfont = font.SysFont('Arial', 20)
        self.img_creditin = myfont.render('Leonardo, Alisson e Danilo', True, (255,255,255))
        self.rect_creditin = self.img_creditin.get_rect(center = (WIDTH/2, 300))
        self.img_toexit = myfont.render('Press ESC back to MENU', True, (155,10,20))
        self.rect_toexit = self.img_toexit.get_rect(center = (WIDTH/2, HEIGHT-50))
        screen.blit(self.img_creditin, self.rect_creditin)
        screen.blit(self.img_toexit, self.rect_toexit)
        display.update()


    def event(self, evt):
        """
            Controla os eventos recebidos do jogador.

            Aqui precisamos apenas tratar o clique do mouse, caso o clique for
            em uma opção do menu.

            Devemos apenas retornar objetos do tipo engine.State desse método,
            pois o objeto que retornamos desse método será o novo "State" do jogo.
        """
        if evt.type == MOUSEBUTTONDOWN and evt.button == 1:
            if self.rect_start.collidepoint(evt.pos):
                return self.game.get_next_stage()
            elif self.rect_credits.collidepoint(evt.pos):
                self.show_credits = True
                self.repaint()
            elif self.rect_endgame.collidepoint(evt.pos):
                return engine.Quit(self.game)
        if evt.type == KEYDOWN and evt.key == K_ESCAPE:
            self.show_credits = False
            self.repaint()
            

game = Game()
game.run(Menu(game),SCREEN)
ThreadPoolManager.getInstance().cancelAllSchedules()
